package org.budo.cmbc.sdk.impl;

import lombok.extern.slf4j.Slf4j;
import org.budo.cmbc.sdk.entity.B2eElectNoteDownLoadRequest;
import org.budo.cmbc.sdk.entity.B2eElectNoteDownLoadResponse;
import org.budo.cmbc.sdk.entity.B2eElectNoteInfo;
import org.budo.cmbc.sdk.entity.B2eElectNoteQryRequest;
import org.budo.cmbc.sdk.entity.B2eElectNoteQryResponse;
import org.budo.cmbc.sdk.entity.BatchCostReimbRequest;
import org.budo.cmbc.sdk.entity.BatchCostReimbResponse;
import org.budo.cmbc.sdk.entity.BatchTransferXferRequest;
import org.budo.cmbc.sdk.entity.BatchTransferXferResponse;
import org.budo.cmbc.sdk.entity.CostReimbRequest;
import org.budo.cmbc.sdk.entity.CostReimbResponse;
import org.budo.cmbc.sdk.entity.OtherBankAgentPayRequest;
import org.budo.cmbc.sdk.entity.OtherBankAgentPayResponse;
import org.budo.cmbc.sdk.entity.QryAllBankCodeRequest;
import org.budo.cmbc.sdk.entity.QryAllBankCodeResponse;
import org.budo.cmbc.sdk.entity.QryBalNewRequest;
import org.budo.cmbc.sdk.entity.QryBalNewResponse;
import org.budo.cmbc.sdk.entity.QryBalRequest;
import org.budo.cmbc.sdk.entity.QryBalResponse;
import org.budo.cmbc.sdk.entity.QryBatchCostReimbRequest;
import org.budo.cmbc.sdk.entity.QryBatchCostReimbResponse;
import org.budo.cmbc.sdk.entity.QryBatchXferRequest;
import org.budo.cmbc.sdk.entity.QryBatchXferResponse;
import org.budo.cmbc.sdk.entity.QryDtlRequest;
import org.budo.cmbc.sdk.entity.QryDtlResponse;
import org.budo.cmbc.sdk.entity.QryOthBankAgentPayResRequest;
import org.budo.cmbc.sdk.entity.QryOthBankAgentPayResResponse;
import org.budo.cmbc.sdk.entity.QryOtherBankAgentActBalRequest;
import org.budo.cmbc.sdk.entity.QryOtherBankAgentActBalResponse;
import org.budo.cmbc.sdk.entity.XferRequest;
import org.budo.cmbc.sdk.entity.XferResponse;
import org.budo.support.http.client.Request;
import org.budo.support.http.client.Response;
import org.budo.support.lang.util.StringUtil;
import org.budo.time.Time;
import org.budo.xml.Xml;
import org.springframework.util.CollectionUtils;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.List;

/**
 * @author lmw
 */
@Slf4j
public class CmbcSdkImpl extends AbstractCmbcSdk {
    private static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";

    @Override
    public QryAllBankCodeResponse qryAllBankCode(QryAllBankCodeRequest request) {
        String xDataBody = "" //
                + "<insId>" + request.getInsId() + "</insId>" //
                + "<localFlag>" + request.getLocalFlag() + "</localFlag>" //
                + "<startNo>" + request.getStartNo() + "</startNo>" //
                + "<queryRows>" + request.getQueryRows() + "</queryRows>";

        String requestXml = this.wrap_xDataBody("qryAllBankCode", xDataBody);
        request.setRequestBody(requestXml);

        String responseXml = this.requestApi(requestXml);

        return new QryAllBankCodeResponse(responseXml);
    }

    @Override
    public BatchTransferXferResponse batchTransferXfer(BatchTransferXferRequest request) {
        String xDataBody = "" //
                + "<trnId>" + request.getTrnId() + "</trnId>" //
                + "<cltcookie></cltcookie>" //
                + "<insId>" + request.getInsId() + "</insId>" //
                + "<payerAcct>" + request.getPayerAcct() + "</payerAcct>" //
                + "<payType>" + request.getPayType() + "</payType>" //
                + "<totalRow>" + request.getTotalRow() + "</totalRow>" //
                + "<totalAmt>" + request.getTotalAmt() + "</totalAmt>" //
                + "<fileContent>" + request.getFileContent() + "</fileContent>";
        String requestXml = this.wrap_xDataBody("batchTransferXfer", xDataBody);
        request.setRequestBody(requestXml);

        String responseXml = this.requestApi(requestXml);

        return new BatchTransferXferResponse(responseXml);
    }

    @Override
    public QryBatchXferResponse qryBatchXfer(QryBatchXferRequest request) {
        String xDataBody = "" //
                + "<trnId>" + request.getTrnId() + "</trnId>" //
                + "<cltcookie></cltcookie>" //
                + "<insId>" + request.getInsId() + "</insId>" //
                + "<payType>" + request.getPayType() + "</payType>";

        String requestXml = this.wrap_xDataBody("qryBatchXfer", xDataBody);
        String responseXml = this.requestApi(requestXml);

        return new QryBatchXferResponse(responseXml);
    }

    @Override
    public XferResponse Xfer(XferRequest request) {
        String xDataBody = "" //
                + "<trnId>" + request.getTrnId() + "</trnId>" //
                + "<cltcookie></cltcookie>" //
                + "<insId>" + request.getInsId() + "</insId>" //
                + "<acntNo>" + request.getAcntNo() + "</acntNo>" //
                + "<acntToNo>" + request.getAcntToNo() + "</acntToNo>" //
                + "<acntToName>" + request.getAcntToName() + "</acntToName>" //
                + "<externBank>" + request.getExternBank() + "</externBank>" //
                + "<bankName>" + request.getBankName() + "</bankName>" //
                + "<amount>" + request.getAmount() + "</amount>";

        // 汇路
        if (StringUtil.isNotNullOrEmpty(request.getLocalFlag())) {
            xDataBody += "<localFlag>" + request.getLocalFlag() + "</localFlag>";
        }

        if (StringUtil.isNotNullOrEmpty(request.getBankCode())) {
            xDataBody += "<bankCode>" + request.getBankCode() + "</bankCode>";
        }

        String requestXml = this.wrap_xDataBody("Xfer", xDataBody);
        request.setRequestBody(requestXml);

        String responseXml = this.requestApi(requestXml);

        return new XferResponse(responseXml);
    }

    @Override
    public QryDtlResponse qryDtl(QryDtlRequest request) {
        String acntNo = null == request.getAcntNo() ? this.getAcntNo() : request.getAcntNo();

        if (null == acntNo) {
            log.error("#124 qryDtl acntNo is null, request=" + request + ", this=" + this);
            return null;
        }

        String xDataBody = "" //
                + "<trnId>" + request.getTrnId() + "</trnId>" //
                + "<cltcookie></cltcookie>" //
                + "<acntNo>" + acntNo + "</acntNo>" //
                + "<dateFrom>" + request.getDateFrom() + "</dateFrom>" //
                + "<dateTo>" + request.getDateTo() + "</dateTo>" //
                + "<startNo>" + request.getStartNo() + "</startNo>" //
                + "<endNo>" + request.getEndNo() + "</endNo>" //
                + "<typeCode>0</typeCode>";

        String requestXml = this.wrap_xDataBody("qryDtl", xDataBody);
        request.setRequestBody(requestXml);

        String responseXml = this.requestApi(requestXml);

        QryDtlResponse response = new QryDtlResponse(responseXml);
        if ("查询账号列表为空或者交易号不存在".equals(response.getMessage())) {
            log.info("#138 qryDtl error, requestXml=" + Xml.format(requestXml) + ", responseXml=" + responseXml);
            return response;
        }

        if (null == response.getDtlInfos()) {
            log.error("#143 qryDtl error, requestXml=" + Xml.format(requestXml) + ", responseXml=" + responseXml);
            return response;
        }

        return response;
    }

    @Override
    public QryBalResponse qryBal(QryBalRequest request) {
        String xDataBody = "" //
                + "<trnId>" + request.getTrnId() + "</trnId>" //
                + "<acntList>";
        for (String acntNo : request.getAcntList()) {
            xDataBody += "<acntNo>" + acntNo + "</acntNo>";
        }
        xDataBody += "</acntList>";

        String requestXml = this.wrap_xDataBody("qryBal", xDataBody);
        request.setRequestBody(requestXml);

        String responseXml = this.requestApi(requestXml);

        return new QryBalResponse(responseXml);
    }

    @Override
    public QryBalNewResponse qryBalNew(QryBalNewRequest request) {
        String xDataBody = "" //
                + "<trnId>" + request.getTrnId() + "</trnId>" //
                + "<acntList>";
        for (String acntNo : request.getAcntList()) {
            xDataBody += "<acntNo>" + acntNo + "</acntNo>";
        }
        xDataBody += "</acntList>";

        String requestXml = this.wrap_xDataBody("qryBalNew", xDataBody);
        request.setRequestBody(requestXml);

        String responseXml = this.requestApi(requestXml);

        return new QryBalNewResponse(responseXml);
    }

    @Override
    public void detailLoad() {
        String xDataBody = "" //
                + "<trnId>111</trnId>" //
                + "<cltcookie></cltcookie>" //
                + "<acntNo>600033029</acntNo>" //
                + "<dateFrom>2019-09-31</dateFrom>" //
                + "<dateTo>2019-10-30</dateTo>" //
                + "<startNo>1</startNo>" //
                + "<queryRows>99</queryRows>" //
                + "<typeCode>0</typeCode>";
        String xml_qryBalNew = this.wrap_xDataBody("detailLoad", xDataBody);
        this.requestApi(xml_qryBalNew);
    }

    @Override
    public B2eElectNoteQryResponse b2eElectNoteQry(B2eElectNoteQryRequest request) {
        int countSum = 0;
        int pageNo = 1;
        int _totalCount = 0;
        request.setPageSize(50);

        List<B2eElectNoteInfo> _b2eElectNoteInfoList = new ArrayList<>();

        do {
            try {
                request.setPageNo(pageNo);

                String xDataBody = "" + "<insId>" + request.getInsId() + "</insId>" //
                        + "<saAcctNo>" + request.getSaAcctNo() + "</saAcctNo>" //
                        + "<BeginDate>" + request.getBeginDate() + "</BeginDate>" //
                        + "<EndDate>" + request.getEndDate() + "</EndDate>" //
                        + "<PrintState>" + request.getPrintState() + "</PrintState>" //
                        + "<MinMoney></MinMoney>" //
                        + "<MaxMoney></MaxMoney>" //
                        + "<pageNo>" + request.getPageNo() + "</pageNo>" //
                        + "<pageSize>" + request.getPageSize() + "</pageSize>"; //

                String requestXml = this.wrap_xDataBody("b2eElectNoteQryNew", xDataBody);
                request.setRequestBody(requestXml);

                String responseXml = this.requestApi(requestXml);

                B2eElectNoteQryResponse _b2eElectNoteQryResponse = new B2eElectNoteQryResponse(responseXml);

                List<B2eElectNoteInfo> b2eElectNoteInfoList = _b2eElectNoteQryResponse.getB2eElectNoteInfoList();

                _totalCount = _b2eElectNoteQryResponse.getCount();
                countSum += b2eElectNoteInfoList.size();

                if (!CollectionUtils.isEmpty(b2eElectNoteInfoList)) {
                    for (B2eElectNoteInfo noteInfo : b2eElectNoteInfoList) {
                        if (noteInfo.getSvrId() != null && noteInfo.getSvrId().startsWith(request.getInsId())) {
                            _b2eElectNoteInfoList.add(noteInfo);
                            return new B2eElectNoteQryResponse(_b2eElectNoteInfoList, _totalCount);
                        }
                    }
                }

                pageNo++;
            } catch (Exception e) {
                log.error("#154 b2eElectNoteQry error, e=" + e, e);
            }
        } while (countSum < _totalCount);

        return new B2eElectNoteQryResponse(_b2eElectNoteInfoList, _totalCount);
    }

    @Override
    public B2eElectNoteDownLoadResponse b2eElectNoteDownLoad(B2eElectNoteDownLoadRequest request) {
        String xDataBody = "" + "<insId>" + request.getInsId() + "</insId>" //
                + "<innerNo>" + request.getInnerNo() + "</innerNo>" //
                + "<xmlFileName>" + request.getXmlFileName() + "</xmlFileName>";

        String requestXml = this.wrap_xDataBody("b2eElectNoteDownLoad", xDataBody);
        request.setRequestBody(requestXml);

        String responseXml = this.requestApi(requestXml);

        return new B2eElectNoteDownLoadResponse(responseXml);
    }

    @Override
    public CostReimbResponse CostReimb(CostReimbRequest request) {
        String xDataBody = "" + "<trnId>" + request.getTrnId() + "</trnId>" // 唯一标志
                + "<insId>" + request.getInsId() + "</insId>" // 业务标识
                + "<acntNo>" + request.getAcntNo() + "</acntNo>" // 付款账号
                + "<acntToNo>" + request.getAcntToNo() + "</acntToNo>" // 收款账号
                + "<acntToName>" + request.getAcntToName() + "</acntToName>" // 收款人姓名
                + "<payeeAcctType>" + request.getPayeeAcctType() + "</payeeAcctType>" // 1--卡 2-- 活折
                + "<amount>" + request.getAmount() + "</amount>" // 金额
                + "<explain>" + request.getExplain() + "</explain>" // 摘要
                + "<certNo>" + request.getCertNo() + "</certNo>"; // 自制凭证号

        String requestXml = this.wrap_xDataBody("CostReimb", xDataBody);
        request.setRequestBody(requestXml);

        String responseXml = this.requestApi(requestXml);

        return new CostReimbResponse(responseXml);
    }

    @Override
    public BatchCostReimbResponse batchCostReimb(BatchCostReimbRequest request) {
        log.info("#289 batchCostReimb, request=" + request);

        String xDataBody = "" + "<trnId>" + request.getTrnId() + "</trnId>" // 唯一标识
                + "<insId>" + request.getInsId() + "</insId>" // 流水号
                + "<PayerAcNo>" + request.getPayerAcNo() + "</PayerAcNo>" // 付款账号
                + "<payType>" + request.getPayType() + "</payType>" // 1:代发工资 2:费用报销
                + "<totalRow>" + request.getTotalRow() + "</totalRow>" // 总行数
                + "<totalAmt>" + request.getTotalAmt() + "</totalAmt>" // 中金额
                + "<fileContent>" + request.getFileContent() + "</fileContent>" // 内容
                + "<Usage>" + request.getUsage() + "</Usage>"; // 用途代码|备注

        String requestXml = this.wrap_xDataBody("batchCostReimb", xDataBody);
        request.setRequestBody(requestXml);

        log.info("#300 batchCostReimb, requestXml=" + Xml.format(requestXml));

        String responseXml = this.requestApi(requestXml);

        BatchCostReimbResponse response = new BatchCostReimbResponse(responseXml);
        if (!"ok".equals(response.getMessage()) || null == response.getTrnId() || request.getTrnId().trim().isEmpty()) {
            log.error("#297 batchCostReimb error, responseXml=" + responseXml + ", requestXml=" + Xml.format(requestXml));
            return response;
        }

        log.info("#301 batchCostReimb ok, responseXml=" + Xml.format(responseXml) + ", requestXml=" + Xml.format(requestXml));
        return response;
    }

    @Override
    public OtherBankAgentPayResponse otherBankAgentPay(OtherBankAgentPayRequest request) {
        String xDataBody = "" + "<trnId>" + request.getTrnId() + "</trnId>" // 唯一标识
                + "<insId>" + request.getInsId() + "</insId>" // 流水号
                + "<vAcctNo>" + request.getVAcctNo() + "</vAcctNo>" // 流水号
                + "<certNo>" + request.getCertNo() + "</certNo>" // 企业自制企业自制凭证号(只能输入数字)
                + "<totalRow>" + request.getTotalRow() + "</totalRow>" // 总行数
                + "<totalAmt>" + request.getTotalAmt() + "</totalAmt>" // 中金额
                + "<fileContent>" + request.getFileContent() + "</fileContent>" // 内容
                + "<usage>" + request.getUsage() + "</usage>" // 内容
                + "<remark>" + request.getRemark() + "</remark>"; // 用途代码|备注

        String requestXml = this.wrap_xDataBody("otherBankAgentPay", xDataBody);
        request.setRequestBody(requestXml);

        log.info("#322 otherBankAgentPay, requestXml=" + Xml.format(requestXml));

        String responseXml = this.requestApi(requestXml);

        OtherBankAgentPayResponse response = new OtherBankAgentPayResponse(responseXml);

        log.info("#325 otherBankAgentPay, requestXml=" + Xml.format(requestXml) + ", responseXml=" + Xml.format(responseXml));
        return response;
    }

    @Override
    public QryOthBankAgentPayResResponse qryOthBankAgentPayRes(QryOthBankAgentPayResRequest request) {
        String xDataBody = "" + "<trnId>" + request.getTrnId() + "</trnId>" //
                + "<insId>" + request.getInsId() + "</insId>";

        String requestXml = this.wrap_xDataBody("qryOthBankAgentPayRes", xDataBody);
        request.setRequestBody(requestXml);

        log.debug("#346 qryOthBankAgentPayRes, requestXml={}" + Xml.lazyFormat(requestXml));

        String responseXml = this.requestApi(requestXml);

        QryOthBankAgentPayResResponse response = new QryOthBankAgentPayResResponse(responseXml);

        // 常见情况 减少日志
        if (null != response && "对账接口报错：文件正在处理中".equals(response.getMessage())) {
            log.info("#352 qryOthBankAgentPayRes, request=" + request + ", response.message=" + response.getMessage());
            return response;
        }

        // 其他情况
        log.info("#352 qryOthBankAgentPayRes, requestXml=" + Xml.format(requestXml) + ", responseXml=" + Xml.format(responseXml));
        return response;
    }

    @Override
    public QryOtherBankAgentActBalResponse qryOtherBankAgentActBal(QryOtherBankAgentActBalRequest request) {
        String xDataBody = "" + "<trnId>" + request.getTrnId() + "</trnId>" //
                + "<qryFlag>" + request.getQryFlag() + "</qryFlag>" //
                + "<startDate>" + request.getStartDate() + "</startDate>" //
                + "<endDate>" + request.getEndDate() + "</endDate>" //
                + "<currentIndex>" + request.getCurrentIndex() + "</currentIndex>" //
                + "<pageSize>" + request.getPageSize() + "</pageSize>";

        String requestXml = this.wrap_xDataBody("qryOtherBankAgentActBal", xDataBody);
        request.setRequestBody(requestXml);

        log.info("#352 qryOtherBankAgentActBal, requestXml=" + Xml.format(requestXml));

        String responseXml = this.requestApi(requestXml);

        QryOtherBankAgentActBalResponse response = new QryOtherBankAgentActBalResponse(responseXml);

        log.info("#358 qryOtherBankAgentActBal, requestXml=" + Xml.format(requestXml) + ", responseXml=" + Xml.format(responseXml));
        return response;
    }

    @Override
    public QryBatchCostReimbResponse qryBatchCostReimb(QryBatchCostReimbRequest request) {
        String xDataBody = "" + "<trnId>" + request.getTrnId() + "</trnId>" // 唯一标识
                + "<insId>" + request.getInsId() + "</insId>" // 流水号
                + "<payType>" + request.getPayType() + "</payType>"; // 1:代发工资 2:费用报销

        String requestXml = this.wrap_xDataBody("qryBatchCostReimb", xDataBody);
        request.setRequestBody(requestXml);

        String responseXml = this.requestApi(requestXml);

        QryBatchCostReimbResponse response = new QryBatchCostReimbResponse(responseXml);

        if (null == response || null == response.getMessage()) {
            log.error("#397 qryBatchCostReimb, requestXml=" + Xml.format(requestXml) + ", responseXml=" + Xml.format(responseXml));
            return response;
        }

        if ("对账接口报错：文件正在处理中".equals(response.getMessage())) {
            log.warn("#402 qryBatchCostReimb, request=" + request + ", response.message=" + response.getMessage());
            return response;
        }

        if ("ok".equals(response.getMessage())) {
            log.info("#407 qryBatchCostReimb, request=" + request + ", response.message=" + response.getMessage());
            return response;
        }

        log.info("#411 qryBatchCostReimb, requestXml=" + Xml.format(requestXml) + ", responseXml=" + Xml.format(responseXml));
        return response;
    }

    private String requestApi(String requestXml) {
        Request request = new Request();
        request.setFollowRedirects(false);

        if (null != this.getProxyHost() && null != this.getProxyPort()) {
            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(this.getProxyHost(), this.getProxyPort()));
            request.setProxy(proxy);
        }

        request.setContentType("application/x-NS-BDES");

        request.setUrl(this.getUrl());
        request.setRequestBody(requestXml);
        Response response = request.execute();

        String responseXml = response.getBody(GB2312);
        if (null == responseXml || requestXml.trim().isEmpty()) {
            log.error("#317 responseXml=" + responseXml + ", response.headers=" + response.getHeaders() + ", requestXml=" + Xml.format(requestXml));
            return null;
        }

        if ("Request_Data_Is_Error".equals(responseXml)) {
            log.error("#74 responseXml=" + responseXml + ", response.headers=" + response.getHeaders() + ", requestXml=" + Xml.format(requestXml));
            return null;
        }

        if (requestXml.contains("No backend server available for connection")) {
            log.error("#327 responseXml=" + responseXml + ", response.headers=" + response.getHeaders() + ", requestXml=" + Xml.format(requestXml));
            return null;
        }

        String _responseXml = null;
        try {
            _responseXml = Xml.format(responseXml);
        } catch (Exception e) {
            log.error("#197 parse responseXml error : " + responseXml, e);
            _responseXml = responseXml;
        }

        log.debug("#200 requestApi, requestXml={}, responseXml={}", Xml.lazyFormat(requestXml), _responseXml); // 返回内容太长，日志里不展示完整内容
        return _responseXml;
    }

    private String wrap_xDataBody(String trnCode, String xDataBody) {
        return "<?xml version=\"1.0\" encoding=\"" + this.getEncoding() + "\"?>" //
                + "<CMBC header=\"100\" version=\"100\" security=\"none\" lang=\"chs\" trnCode=\"" + trnCode + "\">" //
                + this.requestHeader() //
                + "<xDataBody>" //
                + xDataBody //
                + "</xDataBody>" //
                + "</CMBC>";
    }

    private String requestHeader() {
        return "<requestHeader>" + //
                "<dtClient>" + Time.now().toString(YYYY_MM_DD_HH_MM_SS) + "</dtClient>" + //
                "<clientId>" + this.getClientId() + "</clientId>" + //
                "<userId>" + this.getUserId() + "</userId>" + //
                "<userPswd>" + this.getUserPswd() + "</userPswd>" + //
                "<language>" + this.getLanguage() + "</language>" + //
                "  <appId>" + this.getAppId() + "</appId>" + //
                "<appVer>" + this.getAppVer() + "</appVer>" + //
                "</requestHeader>";
    }

    @Override
    public String toString() {
        return this.getClass().getName() + "@" + Integer.toHexString(this.hashCode()) //
                + ", super=" + super.toString();
    }
}
